/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkImageSampling_DEFINED
#define SkImageSampling_DEFINED

#include "include/core/SkTypes.h"

#include <algorithm>
#include <new>

enum class SkFilterMode {
    kNearest, // single sample point (nearest neighbor)
    kLinear,  // interporate between 2x2 sample points (bilinear interpolation)

    kLast = kLinear,
};
static constexpr int kSkFilterModeCount = static_cast<int>(SkFilterMode::kLast) + 1;

enum class SkMipmapMode {
    kNone,    // ignore mipmap levels, sample from the "base"
    kNearest, // sample from the nearest level
    kLinear,  // interpolate between the two nearest levels

    kLast = kLinear,
};
static constexpr int kSkMipmapModeCount = static_cast<int>(SkMipmapMode::kLast) + 1;

/*
 * Specify B and C (each between 0...1) to create a shader that applies the corresponding
 * cubic reconstruction filter to the image.
 *
 * Example values:
 * B = 1/3, C = 1/3        "Mitchell" filter
 * B = 0,   C = 1/2        "Catmull-Rom" filter
 *
 * See "Reconstruction Filters in Computer Graphics"
 * Don P. Mitchell
 * Arun N. Netravali
 * 1988
 * https://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf
 *
 * Desmos worksheet https://www.desmos.com/calculator/aghdpicrvr
 * Nice overview https://entropymine.com/imageworsener/bicubic/
 */
struct SkCubicResampler {
    float B, C;

    // Historic default for kHigh_SkFilterQuality
    static constexpr SkCubicResampler Mitchell()
    {
        return { 1 / 3.0f, 1 / 3.0f };
    }
    static constexpr SkCubicResampler CatmullRom()
    {
        return { 0.0f, 1 / 2.0f };
    }
};

struct SK_API SkSamplingOptions {
    const int maxAniso = 0;
    const bool useCubic = false;
    const SkCubicResampler cubic = { 0, 0 };
    const SkFilterMode filter = SkFilterMode::kNearest;
    const SkMipmapMode mipmap = SkMipmapMode::kNone;

    constexpr SkSamplingOptions() = default;
    SkSamplingOptions(const SkSamplingOptions &) = default;
    SkSamplingOptions &operator = (const SkSamplingOptions &that)
    {
        this->~SkSamplingOptions(); // A pedantic no-op.
        new (this) SkSamplingOptions(that);
        return *this;
    }

    constexpr SkSamplingOptions(SkFilterMode fm, SkMipmapMode mm) : filter(fm), mipmap(mm) {}

    // These are intentionally implicit because the single parameter clearly conveys what the
    // implicitly created SkSamplingOptions will be.
    constexpr SkSamplingOptions(SkFilterMode fm) : filter(fm), mipmap(SkMipmapMode::kNone) {}

    constexpr SkSamplingOptions(const SkCubicResampler &c) : useCubic(true), cubic(c) {}

    static constexpr SkSamplingOptions Aniso(int maxAniso)
    {
        return SkSamplingOptions{ std::max(maxAniso, 1) };
    }

    bool operator == (const SkSamplingOptions &other) const
    {
        return maxAniso == other.maxAniso && useCubic == other.useCubic && cubic.B == other.cubic.B &&
            cubic.C == other.cubic.C && filter == other.filter && mipmap == other.mipmap;
    }
    bool operator != (const SkSamplingOptions &other) const
    {
        return !(*this == other);
    }

    bool isAniso() const
    {
        return maxAniso != 0;
    }

private:
    constexpr SkSamplingOptions(int maxAniso) : maxAniso(maxAniso) {}
};

#endif
